const qiniu = require("qiniu");
const axios = require("axios");
const fs = require("fs");

class QiniuManager {
    constructor(accessKey, secretKey, bucket) {
        // 生成鉴权对象
        this.mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
        // 桶名
        this.bucket = bucket;

        // 初始化配置类
        this.config = new qiniu.conf.Config();
        // 空间对应的机房
        this.config.zone = qiniu.zone.Zone_z0;

        this.bucketManager = new qiniu.rs.BucketManager(this.mac, this.config);
    }

    // 公共回调
    _handleCallback(resolve, reject) {
        return (respErr, respBody, respInfo) => {
            // console.log("respErr", respErr);
            // console.log("respBody", respBody);
            // console.log("respInfo", respInfo);
            if (respErr) {
                throw respErr;
            }

            if (respInfo.statusCode === 200) {
                resolve(respBody);
                // console.log(respBody); // { hash: 'FumyoGBlSqyHPB12hnc031pI1dZK', key: '1.md' }
            } else {
                reject({
                    statusCode: respInfo.statusCode,
                    body: respBody,
                });
            }
        };
    }

    // 获取 publicBucketDomain
    getBucketDomain() {
        const reqURL = `http://uc.qbox.me/v2/domains?tbl=${this.bucket}`;
        const digest = qiniu.util.generateAccessToken(this.mac, reqURL);
        return new Promise((resolve, reject) => {
            qiniu.rpc.postWithoutForm(reqURL, digest, this._handleCallback(resolve, reject));
        });
    }

    // 通过key，获取文件下载链接
    generateDownloadLink(key) {
        // 防止重复请求。 把 publicBucketDomain 存起来，有就取出，没有就重新请求
        const domainPromise = this.publicBucketDomain
            ? Promise.resolve([this.publicBucketDomain])
            : this.getBucketDomain();
        return domainPromise.then((data) => {
            if (Array.isArray(data) && data.length > 0) {
                // 正则判断是否带有http 有就直接取出，没有就拼接上再取出
                const pattern = /^https?/;
                this.publicBucketDomain = pattern.test(data[0]) ? data[0] : "http://" + data[0];
                return this.bucketManager.publicDownloadUrl(this.publicBucketDomain, key);
            } else {
                throw Error("域名未找到，请查看存储空间是否过期");
            }
        });
    }

    // 下载文件
    downloadFile(key, downloadPath) {
        // 1. 获取下载链接
        // 2. 发送请求，返回一个可读流
        // 3. 创建一个可写流，把内容导进去
        // 4.  返回一个promise result
        return this.generateDownloadLink(key).then((link) => {
            // 获取时间戳
            const timeStamp = new Date().getTime();
            // link拼上时间戳
            const url = `${link}?timestamp=${timeStamp}`;
            return axios({
                url,
                method: "GET",
                responseType: "stream",
                headers: {
                    "Cache-Control": "no-cache", // 禁用缓存
                },
            })
                .then((res) => {
                    // 创建一个可写流
                    const writer = fs.createWriteStream(downloadPath);
                    res.data.pipe(writer);
                    return new Promise((resolve, reject) => {
                        writer.on("finish", resolve);
                        writer.on("error", reject);
                    });
                })
                .catch((err) => {
                    return Promise.reject({ err: err.response });
                });
        });
    }

    // 上传方法
    // key 上传后的文件名
    // localFilePath 本地文件路径
    uploadFile(key, localFilePath) {
        const options = {
            scope: this.bucket + ":" + key,
        };
        const putPolicy = new qiniu.rs.PutPolicy(options);
        const uploadToken = putPolicy.uploadToken(this.mac);
        const formUploader = new qiniu.form_up.FormUploader(this.config);
        const putExtra = new qiniu.form_up.PutExtra();

        return new Promise((resolve, reject) => {
            formUploader.putFile(uploadToken, key, localFilePath, putExtra, this._handleCallback(resolve, reject));
        });
    }

    // 删除文件
    deleteFile(key) {
        return new Promise((resolve, reject) => {
            this.bucketManager.delete(this.bucket, key, this._handleCallback(resolve, reject));
        });
    }

    // 判断文件是否存在云中
    getStat(key) {
        return new Promise((resolve, reject) => {
            this.bucketManager.stat(this.bucket, key, this._handleCallback(resolve, reject));
        });
    }
}

module.exports = QiniuManager;
